<template>
  <div class="amap-poly-manager">
    <a-space style="display: flex; justify-content: flex-start; align-items: center; flex-wrap: nowrap">
      <a-button v-if="isPolyExist" :disabled="isDrawing || isEditing" type="primary" @click="redraw">重绘</a-button>
      <a-button
        v-if="!isPolyExist"
        :danger="isDrawing"
        :type="isDrawing ? 'default' : 'primary'"
        @click="() => draw(!isDrawing)"
        >{{ isDrawing ? '结束' : '绘制' }}</a-button
      >
      <a-button v-if="isPolyExist" :disabled="isDrawing || isEditing" danger type="primary" @click="clear"
        >清除</a-button
      >
      <a-button
        v-if="isPolyExist"
        :danger="isEditing"
        :type="isEditing ? 'default' : 'primary'"
        @click="() => modify(!isEditing)"
        >{{ isEditing ? '结束' : '编辑' }}</a-button
      >
      <a-button :loading="loadingOther" :type="isOtherPolyShow ? 'default' : 'primary'" @click="showAll">{{
        `${isOtherPolyShow ? '隐藏' : '显示'}上级和同级`
      }}</a-button>
    </a-space>
    <span v-if="isDrawing" class="amap-poly-manager-tips">双击鼠标左键或单击鼠标右键结束绘制</span>
  </div>
</template>

<script>
import { isEqual, isObject, debounce, get } from 'lodash-es';
import { AMapMouseTool } from '../js/AMapMouseTool.js';
import { AMapPolyEditor } from '../js/AMapPolyEditor.js';
import { getLngLat } from '../js/AMapUtils.js';
import { handleCommonRes } from '../../../utils/resolver.js';
import { getSystemAreaRegionCoordinate } from '../../../api/index.js';
import convertor from '../../../utils/convertor.js';

const defaultPolyOption = {
  fillOpacity: 0.2,
  fillColor: '#3aa1fa',
  strokeOpacity: 0.8,
  strokeColor: '#3aa1fa',
};

export default {
  name: 'AMapPolyManager',
  components: {},
  emits: ['update:modelValue', 'change', 'drawStart', 'drawEnd', 'editStart', 'editEnd'],
  props: {
    mapGetter: { required: true, type: Function },
    /* modelValue: { path: [], region_id: '', region_type: '',... } */
    modelValue: { type: Object, default: undefined },
    area_code: { type: String, default: undefined },
    preview: { type: Boolean, default: false }, // 预览模式
    formatInput: { type: Function, default: (v) => v },
    formatOutput: { type: Function, default: (v) => v },
    formatParams: { type: Function, default: (v) => v },
  },
  data() {
    return {
      isDrawing: false,
      isEditing: false,
      isPolyExist: false,
      isOtherPolyShow: false,
      loadingOther: false,
    };
  },
  created() {
    this.map = this.mapGetter();
    this.AMapMouseTool = new AMapMouseTool(this.map, {
      onStart: () => {
        this.isDrawing = true;
        this.$emit('drawStart');
      },
      onEnd: (e) => {
        if (e && e.obj) {
          this.setPoly(e.obj);
          this.output();
        }
        this.isDrawing = false;
        this.$emit('drawEnd');
      },
    });
    this.AMapPolyEditor = new AMapPolyEditor(this.map, {
      onStart: () => {
        this.isEditing = true;
        this.$emit('editStart');
      },
      onEnd: (e) => {
        if (e && e.target) {
          this.setPoly(e.target);
          this.output();
        }
        this.isEditing = false;
        this.$emit('editEnd');
      },
    });
    this.$watch(
      () => this.modelValue,
      debounce(function (value) {
        if (typeof this.formatInput === 'function') {
          value = this.formatInput(value);
        }
        if (value && isObject(value) && !isEqual(value, this.option)) {
          if (value.path && value.path.length) {
            this.create(value);
          }
          this.update(value);
        }
      }, 300),
      { immediate: true, deep: true }
    );
  },
  methods: {
    output() {
      let value = Object.assign({}, this.option);
      if (typeof this.formatOutput === 'function') {
        value = this.formatOutput(value);
      }
      this.$emit('update:modelValue', value);
      setTimeout(() => {
        this.$emit('change', value);
      }, 0);
    },
    create() {
      if (!this.polyObj) {
        this.polyObj = new AMap.Polygon({ bubble: true });
        this.polyObj.setMap(this.map);
        this.isPolyExist = true;
      }
    },
    setPoly(polyObj) {
      if (polyObj === null) {
        if (this.polyObj) {
          this.polyObj.setMap(null);
        }
        if (this.option && 'path' in this.option) {
          delete this.option.path;
        }
        this.polyObj = null;
        this.isPolyExist = false;
        return;
      }
      if (polyObj instanceof AMap.Polygon) {
        if (this.polyObj) {
          this.polyObj.setMap(null);
        }
        this.polyObj = polyObj;
        this.polyObj.setMap(this.map);
        this.isPolyExist = true;
        this.update(Object.assign({}, this.option, { path: polyObj.getPath() }));
      }
    },
    update(option) {
      if (option && typeof option === 'object') {
        option = Object.assign({}, defaultPolyOption, option, this.getPolyColors(option));
        if (option.path && option.path.length) {
          option.path = option.path.map((i) => getLngLat(i)).filter(Boolean);
        }
        this.option = option;
        if (this.isEditing) {
          this.modify(false);
        }
        if (this.polyObj) {
          this.polyObj.setOptions(this.option);
        }
      }
    },
    redraw() {
      this.clear();
      this.draw(true);
    },
    draw(drawing) {
      if (!drawing) {
        this.AMapMouseTool.stop().then((ok) => {
          if (ok !== false) this.isDrawing = false;
        });
      } else {
        this.AMapMouseTool.start('polygon', this.getPolyColors());
        this.isDrawing = true;
      }
    },
    modify(editing) {
      this.isEditing = !!editing;
      if (this.isEditing) {
        this.AMapPolyEditor.start(this.polyObj);
      } else {
        this.AMapPolyEditor.stop();
      }
    },
    clear() {
      this.setPoly(null);
      this.output();
    },
    getPolyColors(option) {
      option = (isObject(option) && option) || this.option || {};
      return {
        fillColor: option.fillColor || option.strokeColor || defaultPolyOption.fillColor,
        strokeColor: option.strokeColor || option.fillColor || defaultPolyOption.strokeColor,
      };
    },
    async showAll() {
      if (!this.otherPolys || !this.otherPolys.length) {
        this.loadingOther = true;
        const region_id = get(this.modelValue, 'region_id');
        const regionType = get(this.modelValue, 'region_type');
        const region_type = get(regionType, 'value') || regionType;
        let params = {
          area_code: this.area_code,
          range_type: 4,
          region_type: region_type || 1,
          region_id: region_id,
          is_contain_oneself: 0,
        };
        if (typeof this.formatParams === 'function') {
          params = this.formatParams(params);
        }
        const otherArea = await handleCommonRes(getSystemAreaRegionCoordinate, params);
        this.otherPolys = otherArea.reduce((res, item) => {
          if (!item || item.region_id === region_id) {
            return res;
          }
          let path = [];
          if (Array.isArray(item.coordinate)) {
            path = item.coordinate.reduce((r, o) => {
              if (!o || !o.lng || !o.lat) {
                return r;
              }
              let gd = convertor.bd2gd(o.lng, o.lat);
              r.push([gd[0], gd[1]]);
              return r;
            }, []);
          }
          if (!path.length) {
            return res;
          }
          const polyOption = {
            fillColor: item.region_color || '#ff00ff',
            strokeColor: item.region_color || '#ff00ff',
          };
          res.push(new AMap.Polygon({ path, ...defaultPolyOption, ...polyOption }));
          return res;
        }, []);
        this.loadingOther = false;
      }
      if (!this.otherPolys || !this.otherPolys.length) {
        return this.$message.error('当前没有上级和同级的区域数据!');
      }
      const map = this.isOtherPolyShow ? null : this.map;
      this.otherPolys.forEach((poly) => poly.setMap(map));
      this.isOtherPolyShow = !this.isOtherPolyShow;
    },
  },
};
</script>

<style scoped>
.amap-poly-manager {
  .amap-poly-manager-tips {
    position: absolute;
    bottom: -30px;
    left: 10px;
    right: 10px;
    z-index: 10;
    margin: auto;
    padding: 4px 20px;
    border-radius: 4px;
    background: rgba(231, 88, 88, 0.2);
    text-align: center;
    color: #e75858;
  }
}
</style>
